# /**
# * Copyright (c) 2022 Beijing Jiaotong University
# * PhotLab is licensed under [Open Source License].
# * You can use this software according to the terms and conditions of the [Open Source License].
# *
# * You may obtain a copy of [Open Source License] at: [https://open.source.license/]
# *
# * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
# *
# * See the [Open Source License] for more details.
# */
# /**
# * Author: Shihao Xie
# * Created: Jul. 5, 2022
# * Supported by: National Key Research and Development Program of China
# */

from phot_server.core.api_injector import api
from phot_server.manager.engine_manager import EngineManager
from phot_server.rpc.engine_service.Engine import Processor


# gettext instructions
# D:/Python/Tools/i18n/
# pygettext.py -d engine -o locales/engine.po service/engine_service.py
# msgfmt.py -o ./locales/cn/LC_MESSAGES/engine.mo ./locales/cn/LC_MESSAGES/engine
# msgfmt.py -o ./locales/en/LC_MESSAGES/engine.mo ./locales/en/LC_MESSAGES/engine

#
# This class contains all interface function of engine
#
class EngineService:
    """
    该服务是计算引擎相关服务，可以通过这个服务管理用户拥有的仿真任务。
    调用该服务前，请确认已经做过连接服务相关操作并成功连接。例如：

    Examples:
        >>> from thrift import Thrift
        >>> from thrift.transport import TSocket
        >>> from thrift.transport import TTransport
        >>> from thrift.protocol import TBinaryProtocol
        >>> from photlab.engine_service.Engine import Client as engine_client
        >>> transport = TSocket.TSocket("localhost", 8080)
        >>> transport_protocol = TTransport.TBufferedTransport(transport)
        >>> protocol_factory = TBinaryProtocol.TBinaryProtocol(transport_protocol)
        >>> engine_client = engine_client(protocol_factory)
        >>> transport.open()

    具体使用可以查看测试用例 test/test_interface/test_engine.py
    """
    def __init__(self):
        self.engine_manager = EngineManager()

    @api(Processor.process_add_task)
    def add_task(self, graph, task_id, priority=None, execution_time=None):
        """
        给服务端一个PhotGraph计算图，服务端将根据计算图创建一个仿真任务，并将该任务添加到计算引擎中。

        Args:
            graph (PhotGraph) : 计算图
            task_id (string): 任务id，任务id由用户决定
            priority (int): 任务优先级，默认为 3(LOW) [GOD = 0，URGENT = 1，MIDDLE = 2，LOW = 3]
            execution_time (long): 开始执行时间，默认为立即开始

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> graph = PhotGraph()
            >>> engine_client.add_task(graph, "task_1", None, None)
        """
        import time
        t1 = time.time()
        self.engine_manager.add_task(graph, task_id, priority, execution_time)
        t2 = time.time()
        print(t2-t1)

    @api(Processor.process_create_task_from_existing_graph)
    def create_task_from_existing_graph(self, graph_name, task_id, priority=None, execution_time=None):
        """
        服务端将根据计算图名称，从已有的计算图中创建一个仿真任务，并将该任务添加到计算引擎中。

        Args:
            graph_name (string) : 计算图名称，已经打开(open_graph)且并未关闭(close_graph)的计算图名称
            task_id (string): 任务id，任务id由用户决定
            priority (int): 任务优先级，默认为 3(LOW) [GOD = 0，URGENT = 1，MIDDLE = 2，LOW = 3]
            execution_time (long): 开始执行时间，默认为立即开始

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.create_task_from_existing_graph("test_case_2", "task_1", None, None)
        """
        self.engine_manager.create_task_from_existing_graph(graph_name, task_id, priority, execution_time)

    @api(Processor.process_calculate_task)
    def calculate_task(self, task_id):
        """
        服务端将调用计算引擎接口，让计算引擎立即开始进行对应id的仿真任务，将任务状态置为**运行**(PhotTaskStatus = TASK_RUNNING)。

        Args:
            task_id (string): 任务id，已经添加到服务端的任务id

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.calculate_task("task_1")
        """
        self.engine_manager.calculate_task(task_id)

    @api(Processor.process_cancel_task)
    def cancel_task(self, task_id):
        """
        服务端将调用计算引擎接口，让计算引擎立即**取消**以下状态(PhotTaskStatus =)的任务：
            **运行**(TASK_RUNNING)
            **暂停**(TASK_STOPPED)
            **无反应**(TASK_NO_RESPONDING)
            **计算完成**(TASK_ACCOMPLISHED)
            **异常导致结束**(TASK_DIED)
        并清除该任务所有仿真数据，将任务状态置为**创建**(TASK_CREATING)。

        Args:
            task_id (string): 任务id，已经开始计算的任务id

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.cancel_task("task_1")
        """
        self.engine_manager.cancel_task(task_id)

    @api(Processor.process_pause_task)
    def pause_task(self, task_id):
        """
        服务端将调用计算引擎接口，让计算引擎立即**暂停**任务状态为**运行**(TASK_RUNNING)的对应id的仿真任务，
        但是不清除该任务所有仿真数据，将任务状态置为**暂停**(TASK_STOPPED)。

        Args:
            task_id (string): 任务id，已经开始计算的任务id

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.pause_task("task_1")
        """
        self.engine_manager.pause_task(task_id)

    @api(Processor.process_recover_task)
    def recover_task(self, task_id):
        """
        服务端将调用计算引擎接口，让计算引擎立即**恢复运行**任务状态为**暂停**(TASK_STOPPED)的对应id的仿真任务，
        从该任务已有的所有仿真数据开始计算，将任务状态置为**运行**(TASK_RUNNING)。

        Args:
            task_id (string): 任务id，已经暂停的计算的任务id

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.recover_task("task_1")
        """
        self.engine_manager.recover_task(task_id)

    @api(Processor.process_delete_task)
    def delete_task(self, task_id):
        """
        服务端将调用计算引擎接口，让计算引擎立即删除对应id的仿真任务，但是要注意的是，如果任务处于以下状态(PhotTaskStatus =)：
            **运行**(TASK_RUNNING)
            **暂停**(TASK_STOPPED)
            **无反应**(TASK_NO_RESPONDING)
            **异常导致结束**(TASK_DIED)
        则删除任务失败!

        Args:
            task_id (string): 任务id，需要删除的任务id

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.delete_task("task_1")
        """
        self.engine_manager.delete_task(task_id)

    @api(Processor.process_restart_task)
    def restart_task(self, task_id):
        """
        服务端将调用计算引擎接口，让计算引擎重新计算对应id的仿真任务，但是要注意的是，如果任务必须处于以下状态(PhotTaskStatus =)：
            **暂停**(TASK_STOPPED)
            **无反应**(TASK_NO_RESPONDING)
            **异常导致结束**(TASK_DIED)
            **计算完成**(TASK_ACCOMPLISH)

        Args:
            task_id (string): 任务id，需要重新计算的任务id

        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.delete_task("task_1")
        """
        self.engine_manager.restart_task(task_id)

    @api(Processor.process_get_component_output)
    def get_component_output(self, task_id, component_id):
        """
        服务端将调用计算引擎接口，从计算引擎获取任务状态为**计算完成**(TASK_ACCOMPLISHED)的仿真任务中的对应id的组件的输出数据。

        Args:
            task_id (string): 任务id，完成仿真计算的任务id
            component_id(int): 仿真组件id， 输出数据的组件id
        Returns:
            PhotOutput: 仿真组件输出数据
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.get_component_output("task_1", 0)
        """
        return self.engine_manager.get_component_output(task_id, component_id)

    @api(Processor.process_get_analyzer_output)
    def get_analyzer_output(self, task_id, component_id):
        """
        服务端将调用计算引擎接口，从计算引擎获取任务状态为**计算完成**(TASK_ACCOMPLISHED)的仿真任务中的对应id的组件的输出数据。

        Args:
            task_id (string): 任务id，完成仿真计算的任务id
            component_id(int): 仿真组件id， 输出数据的组件id
        Returns:
            PhotOutput: 仿真组件输出数据
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.get_component_output("task_1", 0)
        """
        return self.engine_manager.get_analyzer_outputs(task_id, component_id)

    @api(Processor.process_save_component_output_as)
    def save_component_output_as(self, task_id, component_id, file_path):
        """
        服务端将调用计算引擎接口，从计算引擎获取任务状态为**计算完成**(TASK_ACCOMPLISHED)的仿真任务中的对应id的组件的输出数据，并将数据保存到用户指定的文件。

        Args:
            task_id (string): 任务id，完成仿真计算的任务id
            component_id (int): 仿真组件id， 输出数据的组件id
            file_path (string): 文件绝对路径
        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.save_component_output_as("task_1", 0, "C://user//component_0.txt")
        """
        self.engine_manager.save_component_output_as(task_id, component_id, file_path)

    @api(Processor.process_save_analyzer_result_as)
    def save_analyzer_result_as(self, task_id, component_id, file_path):
        """
        服务端将调用计算引擎接口，从计算引擎获取任务状态为**计算完成**(TASK_ACCOMPLISHED)的仿真任务中的对应id的分析器的输出结果，并将数据保存到用户指定的文件。
        (分析器：属性isAnalyzer为真的组件，PhotComponent(isAnalyzer=True))

        Args:
            task_id (string): 任务id，完成仿真计算的任务id
            component_id (int): 仿真组件id， 输出数据的组件id
            file_path (string): 文件绝对路径
        Returns:
            无
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.save_analyzer_result_as("task_1", 1, "C://user//component_0.txt")
        """
        self.engine_manager.save_analyzer_result_as(task_id, component_id, file_path)

    @api(Processor.process_get_task_status)
    def get_task_status(self, task_id):
        """
        服务端将调用计算引擎接口，从计算引擎获取仿真任务的任务状态(PhotTaskStatus).

        Args:
            task_id (string): 任务id，服务端已有的任务id
        Returns:
            PhotTaskStatus： 仿真任务状态
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.get_task_status("task_1")
        """
        return self.engine_manager.get_task_status(task_id)

    @api(Processor.process_get_task_exception_information)
    def get_task_exception_information(self, task_id):
        """
        服务端将调用计算引擎接口，从计算引擎获取状态为**无反应**(TASK_NO_RESPONDING)和**异常导致结束**(TASK_DIED)的仿真任务的任务异常信息.

        Args:
            task_id (string): 任务id，异常的任务id
        Returns:
            list<string>： 仿真任务异常信息
        Raises:
            PhotError: 异常代码查看PhotError的exception_code
        Examples:
            >>> engine_client.get_task_exception_information("task_1")
        """
        return self.engine_manager.fetch_all_simulation_result(task_id)